package middlewareWeb;

import java.util.*;
/**
 *Classe che modellizza il Transaction Manager
 * @author myusy
 */
public class TransactionMng {
    private boolean OKtimer1 = false;
    private boolean OKtimer2 = false;
    private boolean ENTRATO = false;
    private Hashtable clocks = new Hashtable();

    /**
     * Metodo costruttore
     *
     */
    TransactionMng()
    {
        initClocks();
    }

    /**
     * metodo che inizializza il vector clock locale del transaction manager
     */
    private void initClocks()
    {
        clocks.put("M001",new Integer(0));
        clocks.put("W001",new Integer(0));
        clocks.put("W002",new Integer(0));
        clocks.put("W003",new Integer(0));
        clocks.put("T001",new Integer(0));
        clocks.put("F001",new Integer(0));
    }

    /**
     * metodo che ritorna la variabile OKtimer1
     * @return variabile d'appoggio booleana
     */
    public synchronized boolean getOKtimer1(){
        return this.OKtimer1;
    }
    /**
     * metodo che setta la variabile OKtimer1
     * @param b variabile d'appoggio booleana
     */
    public synchronized void setOKtimer1(boolean b){
        this.OKtimer1 = b;
    }
    /**
     * metodo che ritorna la variabile ENTRATO
     * @return variabile d'appoggio booleana
     */
    public synchronized boolean getENTRATO(){
        return this.ENTRATO;
    }
        /**
     * metodo che setta la variabile ENTRATO
     * @param b variabile d'appoggio booleana
     */
    public synchronized void setENTRATO(boolean b){
        this.ENTRATO = b;
    }
       /**
     * metodo che ritorna la variabile OKtimer2
     * @return variabile d'appoggio booleana
     */
    public synchronized boolean getOKtimer2(){
        return this.OKtimer2;
    }
    /**
     * metodo che setta la variabile OKtimer2
     * @param b variabile d'appoggio booleana
     */
    public synchronized void setOKtimer2(boolean b){
        this.OKtimer2 = b;
    }
    /**
     * metodo che esegue la sottomissione transazionale
     * @param wf variabile d'appoggio booleana
     * @param wn
     * @param proxy
     */
    public void transaction(Workflow wf, WorkerNetwork wn,TQueueManagerProxy proxy){
        /*****************************/
        /* Primo timer: se scade si deve abortire la transazione*/
        Timer timer1 = new Timer();
	TimerTask task1 = new Timer1();
	timer1.schedule( task1,10000 );
        /*****************************/

        /*********************************************/
        /* Thread che si occupa di avviare la transazione*/
        Thread t = new Thread(new EseguiTransazione());
        t.start();
        /*********************************************/

        /********************/
        /* Attesa attiva: La condizione del while
         * diventa false(quindi finisce l'attesa)
         * solo se o ricevo tutti READY o scade il timer1*/
        while(!getOKtimer1()){}
        setOKtimer1(false);
        /********************/

        /* se la condizione dell'if è vera significa
         * che non è scaduto il timer, quindi ho
         * ricevuto tutti i READY-->GLOBAL COMMIT*/
        if (!getENTRATO()){
            timer1.cancel();//Stop timer1

            /**********************************/
            /* Timer2: avvia un thread che si occupa di comunicare la GLOBAL_decision.
             * In questo caso fino a quando non ricevo tutti gli ACK continuo a
             * comunicare la GLOBAL_decision*/
            Timer timer2 = new Timer();
            TimerTask task2 = new TimerCommit();
            timer2.schedule( task2,0,10000 );
            /**********************************/

            /********************/
            /*Attesa attiva: proseguo solo quando ricevo tutti gli ACK*/
            while(!getOKtimer2()){}
            setOKtimer2(false);
            /********************/

            //ho ricevuto tutti gli ack-->TRANSAZIONE CONCLUSA CON COMMIT-->posso andare avanti
            timer2.cancel();//Stop timer2

            /*********************************************************/
            /*ESEGUO IL CODICE GIA' ESISTENTE*/
            int i =0;
            while(i<wf.getTasks().size() && i<wn.getWorkers().size()){

                proxy.setAddr(wn.getWorker(i));
                proxy.enqueue(wf.getTask(i));
                i++;
            }
            /*********************************************************/

        }else{
            /*Stessa logica precedente: in questo caso però devo essere certo
             * che tutti abbiano appreso la GLOBAL ABORT */
            Timer timer2 = new Timer();
            TimerTask task2 = new TimerAbort();

            // aspetta 0 secondi prima dell'esecuzione e poi richiama task2 ogni 10 sec
            timer2.schedule( task2,0,10000 );
            while(!getOKtimer2()){
            }
            setOKtimer2(false);
            //ho ricevuto tutti gli ack-->TRANSAZIONE CONCLUSA CON ABORT-->posso andare avanti
            timer2.cancel();
            System.out.println("GLOBAL ABORT --> TRANSAZIONE FALLITA");
        }
        
    }

  public class EseguiTransazione implements Runnable  {
  /**
     * metodo costruttore
     */
      public EseguiTransazione(){}
        @Override
        public void run() {
            boolean b = beginTransaction();
            setOKtimer1(b);
        }
  }
  /**
 *Classe che modellizza un oggetto di tipo Timer
 * @author myusy
 */
  public class Timer1 extends TimerTask{
        @Override
        public void run() {
            setENTRATO(true);
            setOKtimer1(true);
      }
  }
    /**
 *Classe che modellizza un oggetto di tipo Timer
 * @author myusy
 */
  public class TimerCommit extends TimerTask{
        @Override
        public void run() {
            setOKtimer2(phase2Transaction("GLOBAL_COMMIT"));
        }
    }
    /**
 *Classe che modellizza un oggetto di tipo Timer
 * @author myusy
 */
  public class TimerAbort extends TimerTask{
        @Override
        public void run() {
            setOKtimer2(phase2Transaction("GLOBAL_ABORT"));
        }
  }
   
     /**
     * metodo che richiama il metodo del servizio TransactionManager
     * @param arg0
      * @return
     */
    private static boolean phase2Transaction(java.lang.String arg0) {
        clientWS.TransactionManagerService service = new clientWS.TransactionManagerService();
        clientWS.TransactionManager port = service.getTransactionManagerPort();
        return port.phase2Transaction(arg0);
    }
      /**
     * metodo che richiama il metodo del servizio TransactionManager
      * @return
     */
    private static boolean beginTransaction() {
        clientWS.TransactionManagerService service = new clientWS.TransactionManagerService();
        clientWS.TransactionManager port = service.getTransactionManagerPort();
        return port.beginTransaction();
    }

    

    
}
